﻿//---------------------------------------------------------------------------
// <copyright file="RibbonTitlePanel.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
    #region Using directives

    using System;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;

    #endregion

    /// <summary>
    ///   Implements the title panel of the Ribbon, this layouts out the Ribbon title
    ///   and RibbonContextualTabGroup separators around the QAT and system buttons.
    /// </summary>
    public class RibbonTitlePanel : Panel
    {
        #region Fields

        /// <summary>
        ///   The Ribbon's Title.
        /// </summary>
        private UIElement _title;

        /// <summary>
        ///   The Ribbon's QuickAccessToolbar.
        /// </summary>
        private UIElement _qat;

        #endregion

        #region Public Properties

        /// <summary>
        ///   Gets or sets a placeholder for the QuickAccessToolbar.
        /// </summary>
        public UIElement QuickAccessToolBarPlaceholder
        {
            get
            {
                return _qat;
            }

            set
            {
                if (_qat != value)
                {
                    if (_qat != null)
                    {
                        Children.Remove(_qat);
                    }

                    _qat = value;
                    if (_qat != null)
                    {
                        Children.Add(_qat);
                    }
                }
            }
        }

        /// <summary>
        ///   Gets or sets a placeholder for the Title.
        /// </summary>
        public UIElement TitlePlaceholder
        {
            get
            {
                return _title;
            }

            set
            {
                if (_title != value)
                {
                    if (_title != null)
                    {
                        Children.Remove(_title);
                    }

                    _title = value;
                    if (_title != null)
                    {
                        Children.Add(_title);
                    }
                }
            }
        }

        #endregion

        #region Private Properties

        /// <summary>
        ///   Gets the parent Ribbon control.
        /// </summary>
        private Ribbon Ribbon
        {
            get { return TemplatedParent as Ribbon; }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        ///   Gets a UIElementCollection of the TitlePanel's children.
        /// </summary>
        /// <param name="logicalParent">The logical parent of the children of the collection.</param>
        /// <returns>Returns a UIElementCollection of the TitlePanel's children.</returns>
        protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
        {
            // Ribbon internally adds RibbonContextualTabGroup(s) to Children collection and because
            // RibbonContextualTabGroup logical parent is Ribbon we don't want the panel to set logical panel too.
            return new UIElementCollection(this, TemplatedParent == null ? logicalParent : null);
        }

        /// <summary>
        ///   Measures the children of the RibbonTitlePanel.
        /// </summary>
        /// <param name="constraint">The space available for layout.</param>
        /// <returns>Returns the RibbonTitlePanel's desired size as determined by measurement of its children.</returns>
        protected override Size MeasureOverride(Size constraint)
        {
            Size desiredSize = new Size();
            int count = Children.Count;
            if (count == 0)
            {
                return desiredSize;
            }

            double startContextualTab = constraint.Width;
            double endContextualTab = 0;

            // First priority is to measure RibbonContextualTabGroups if available
            for (int i = 0; i < Children.Count; i++)
            {
                RibbonContextualTabGroup group = Children[i] as RibbonContextualTabGroup;
                if (group != null)
                {
                    double width = 0;
                    group.ArrangeWidth = 0;
                    group.ArrangeX = 0;

                    // Don't measure the child if tabs are not ready yet
                    if (group.DesiredWidth > 0 && Ribbon != null && !Ribbon.IsCollapsed)
                    {
                        double x = this.CalculatePosition(group) - group.Margin.Left;
                        width = Math.Max(0, group.DesiredWidth);

                        // Clip the group on the left side
                        if (x < 0)
                        {
                            width = Math.Max(0, width + x);
                            x = 0;
                        }

                        // If not enough room - clip the group on the right side
                        if (x + width > constraint.Width)
                        {
                            width = Math.Max(0, constraint.Width - x);
                        }

                        if (width > 0)
                        {
                            if (x < startContextualTab)
                            {
                                startContextualTab = x;
                            }

                            if (x + width > endContextualTab)
                            {
                                endContextualTab = x + width;
                            }
                        }

                        // Measure the maximum desired width - we need to determine if we need to show the label tooltip
                        group.Measure(new Size(double.PositiveInfinity, constraint.Height));
                        double maxDesiredWidth = group.DesiredSize.Width;

                        group.ArrangeWidth = width;
                        group.Measure(new Size(width, constraint.Height));

                        // If label is truncated - show the tooltip
                        group.ShowLabelToolTip = DoubleUtil.GreaterThan(maxDesiredWidth, group.DesiredSize.Width);
                    }

                    desiredSize.Width += width;
                    desiredSize.Height = Math.Max(desiredSize.Height, group.DesiredSize.Height);
                }
            }

            // Measure the QuickAccessToolbar
            double qatDesiredWidth = 0.0;
            if (_qat != null)
            {
                _qat.Measure(new Size(startContextualTab, constraint.Height));
                qatDesiredWidth = _qat.DesiredSize.Width;
                desiredSize.Width += qatDesiredWidth;
                desiredSize.Height = Math.Max(desiredSize.Height, _qat.DesiredSize.Height);
            }

            // Measure the title
            if (_title != null)
            {
                endContextualTab = Math.Max(endContextualTab, qatDesiredWidth);
                double leftSpace = startContextualTab - qatDesiredWidth;
                double rightSpace = constraint.Width - endContextualTab;
                double titleSpace = Math.Max(startContextualTab - qatDesiredWidth, desiredSize.Width - endContextualTab);
                if (((desiredSize.Width - titleSpace) * 0.5) < endContextualTab)
                { // Align on left
                    titleSpace = leftSpace;
                }
                else
                {
                    titleSpace = rightSpace;
                }

                _title.Measure(new Size(titleSpace, constraint.Height));
                desiredSize.Width += _title.DesiredSize.Width;
                desiredSize.Height = Math.Max(desiredSize.Height, _title.DesiredSize.Height);
            }

            desiredSize.Width = Math.Min(desiredSize.Width, constraint.Width); // Prevent clipping
            return desiredSize;
        }

        /// <summary>
        ///   Arranges the children of the RibbonTitlePanel.
        /// </summary>
        /// <param name="arrangeSize">The arranged size of the TitlePanel.</param>
        /// <returns>The size at which the TitlePanel was arranged.</returns>
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double x = 0.0;
            double y = 0.0;
            double width;
            double height;
            double startContextualTab = arrangeSize.Width;
            double endContextualTab = 0;
            for (int i = 2; i < Children.Count; i++)
            {
                RibbonContextualTabGroup group = Children[i] as RibbonContextualTabGroup;
                Debug.Assert(group != null, "RibbonContextualTabGroups should never be null");

                width = Math.Max(0, group.ArrangeWidth);
                height = group.DesiredSize.Height;
                x = Math.Max(0, this.CalculatePosition(group) - group.Margin.Left);
                y = arrangeSize.Height - height;

                group.ArrangeX = x;
                group.Arrange(new Rect(x, y, width, Math.Max(0.0, height - 1)));

                if (width > 0)
                {
                    if (x < startContextualTab)
                    {
                        startContextualTab = x;
                    }

                    if (x + width > endContextualTab)
                    {
                        endContextualTab = x + width;
                    }
                }
            }

            // Arrnage QuickAccessToolbar
            double qatDesiredWidth = 0.0;
            if (_qat != null)
            {
                qatDesiredWidth = _qat.DesiredSize.Width;
                _qat.Arrange(new Rect(0, 0.5 * (arrangeSize.Height - _qat.DesiredSize.Height), qatDesiredWidth, _qat.DesiredSize.Height));
            }

            endContextualTab = Math.Max(endContextualTab, qatDesiredWidth);

            // Arrange the title
            if (_title != null)
            {
                x = 0.0;
                y = 0.5 * (arrangeSize.Height - _title.DesiredSize.Height);
                width = 0.0;
                height = 0.0;
                double titleSpace = Math.Max(startContextualTab - qatDesiredWidth, arrangeSize.Width - endContextualTab);
                if (titleSpace > 20.0)
                {
                    width = Math.Min(_title.DesiredSize.Width, titleSpace);
                    height = _title.DesiredSize.Height;
                    if (((arrangeSize.Width - width) * 0.5) < endContextualTab)
                    { // Align on left
                        x = qatDesiredWidth + ((startContextualTab - qatDesiredWidth - width) * 0.5);
                    }
                    else
                    {
                        x = (arrangeSize.Width - width) * 0.5;
                    }
                }

                _title.Arrange(new Rect(x, y, width, height));
            }

            InvalidateVisual(); // Ensure OnRender is called to draw the separators
            return arrangeSize;
        }

        /// <summary>
        ///   Draws separators for the RibbonContextualTabGroups.
        /// </summary>
        /// <param name="dc">The drawing context to use.</param>
        protected override void OnRender(DrawingContext dc)
        {
            double offsetX = 0;
            Brush b = (Brush)FindResource(RibbonSkinResources.RibbonContextualTabSeparatorBrushKey);
            Pen pen = new Pen(b, 1.0);
            int count = Children.Count;

            // For all TabGroup(s)
            for (int i = 2; i < count; i++)
            {
                RibbonContextualTabGroup group = Children[i] as RibbonContextualTabGroup;
                Debug.Assert(group != null, "RibbonContextualTabGroups must not be null");

                offsetX = Math.Max(0, group.ArrangeX - group.Margin.Left);

                if (group.ArrangeWidth > 0)
                {
                    dc.DrawLine(pen, new Point(offsetX, 0), new Point(offsetX, this.ActualHeight + 22));

                    // If group is truncated then group._arrangeWidth < group._desiredWidth and we need to draw separator in 2 parts
                    GeometryGroup g = new GeometryGroup();
                    g.Children.Add(new LineGeometry(new Point(offsetX + group.ArrangeWidth, 0), new Point(offsetX + group.ArrangeWidth, this.ActualHeight)));
                    g.Children.Add(new LineGeometry(new Point(offsetX + group.DesiredWidth, this.ActualHeight), new Point(offsetX + group.DesiredWidth, this.ActualHeight + 22)));
                    dc.DrawGeometry(null, pen, g);
                }
            }

            base.OnRender(dc);
        }

        #endregion

        #region Private Methods

        /// <summary>
        ///   Calculates the x-position at which the given ContextualTabGroup should be placed on the title panel.
        /// </summary>
        /// <param name="group">The RibbonContextualTabGroup to position.</param>
        /// <returns>The x-offset at which the ContextualTabGroup should be positioned.</returns>
        private double CalculatePosition(RibbonContextualTabGroup group)
        {
            double result = 0.0;
            if (group.Tabs.Count > 0 && Ribbon != null)
            {
                RibbonTab tab = group.Tabs[0];
                GeneralTransform transformRibbonTabToRibbon = tab.TransformToAncestor(Ribbon);
                GeneralTransform transformRibbonToRibbonTitlePanel = Ribbon.TransformToDescendant(this);
                Point point = new Point();
                point = transformRibbonTabToRibbon.Transform(point);
                point = transformRibbonToRibbonTitlePanel.Transform(point);
                result = point.X - tab.Margin.Left; // Reduce RibbonTab Margin
            }

            return result;
        }

        #endregion
    }
}